import { Controller, Get, HttpStatus, Param, Res } from "@nestjs/common";
import { Public } from "src/decorators/is-public.decorator";
import type { Response } from "express";
import path from "path";
import fs from 'fs';
import https from 'https';
import { Readable } from "stream";

@Controller('file-stream')
export class FileStreamController {
    /**
     * Return a local file stream by filename
     */
    @Public()
    @Get('local/:filename')
    async streamLocalFile(@Param('filename') filename: string, @Res() res: Response) {
        const filePath = path.join(process.cwd(), 'static', filename);
        // 1: check file exists
        if (!fs.existsSync(filePath)) {
            res.status(404).send('File not found');
            return;
        }
        // 2: get file info
        const fileStat = fs.statSync(filePath);
        const fileSize = fileStat.size;
        console.log("🚀 ~ FileStreamController ~ streamLocalFile ~ fileSize:", fileSize)
        const mimeType = this.getMimeType(filename);

        // 3: set response headers
        res.setHeader('Content-Type', mimeType);
        res.setHeader('Content-Length', fileSize);
        res.setHeader('Content-Disposition', `inline; filename="${filename}"`); // inline 预览 attachment 下载

        // 4: create file stream and pipe responses
        const readStream = fs.createReadStream(filePath, { highWaterMark: 64 * 1024 });
        readStream.pipe(res);

        // 5: handle error
        readStream.on('error', (err) => {
            console.error('Error streaming file:', err);
            res.status(500).send('Internal server error');
        });

        // 6: close client connection when stream ends
        res.on('end', () => {
            readStream.destroy();
        });
    }

    /**
     * Case 2: remote file stream by filename(CDN)
     */
    @Public()
    @Get('remote')
    async streamRemoteFile(@Res() res: Response) {
        const remoteUrl = 'https://fastly.picsum.photos/id/967/2000/1000.jpg?hmac=86G5lZNyi-3PdqitiNNhorV15b6VvY7APfk7suHQEP4';
        https.get(remoteUrl, (remoteRes) => {
            // check remote response status
            if (remoteRes.statusCode !== 200) {
                return res.status(HttpStatus.BAD_GATEWAY).send('Retrieve Remote file failure.')
            }

            // Forward remote response header
            res.setHeader('Content-Type', remoteRes.headers['content-type'] || 'application/octet-stream');
            res.setHeader('Content-Length', remoteRes.headers['content-length'] || '');
            res.setHeader('Content-Disposition', 'inline; filename="remote-file.jpg"');

            // make remote pipe to client response
            remoteRes.pipe(res);

            // handle stream error
            remoteRes.on('error', (err) => {
                console.error('Error streaming remote file:', err);
                res.status(HttpStatus.INTERNAL_SERVER_ERROR).send('Internal server error');
            });

            // CLose connection
            res.on('end', () => {
                remoteRes.destroy();
            });
        })
    }

    @Public()
    @Get('dynamic')
    streamDynamicContent(@Res() res: Response) {
        // 1. set response header
        res.setHeader('Content-Type', 'text/plain');
        res.setHeader('Content-Disposition', 'attachment; filename="dynamic-log.txt"');

        // create readable stream
        const stream = new Readable({
            read() {
                for(let i = 0; i < 1000; i++){
                    this.push('Dynamic content log: ' + new Date().toISOString() + '\n');
                }
                this.push(null); // mark stream end.
            }
        });
        stream.pipe(res);

        res.on('end', () => {
            stream.destroy();
        });
    }

    /**
     * 
     * @param filename 
     * @returns 
     */
    private getMimeType(filename: string) {
        const ext = path.extname(filename);
        switch (ext) {
            case '.jpg':
            case '.jpeg':
                return 'image/jpeg';
            case '.png':
                return 'image/png';
            case '.gif':
                return 'image/gif';
            case '.pdf':
                return 'application/pdf';
            case '.docx':
                return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
            default:
                return 'application/octet-stream';
        }
    }
}